﻿namespace EyeSoft.Test.Security.Cryptography
{
	using System;
	using System.Security.Cryptography;

	using EyeSoft.Security.Cryptography;

	using Microsoft.VisualStudio.TestTools.UnitTesting;

	using SharpTestsEx;

	[TestClass]
	public class HashAlgorithmFactoryTest
	{
		private const string FakeProviderName = "Fake";
		private const string FakeExpectedHash = "RgBhAGsAZQA=";

		[TestInitialize]
		public void Initialize()
		{
			HashAlgorithmFactory.ClearCustomProviders();
		}

		[TestMethod]
		public void UseAnUnknowHashAlgorithmExpectedException()
		{
			Executing
				.This(() => VerifyHashAlgorithm("Unknow", null))
				.Should().Throw<ArgumentNullException>();
		}

		[TestMethod]
		public void VerifyCustomHashAlgorithm()
		{
			HashAlgorithmFactory.Register<FakeHashAlgorithm>(FakeProviderName);

			VerifyHashAlgorithm(FakeProviderName, FakeExpectedHash);
		}

		[TestMethod]
		public void VerifyCustomHashAlgorithmRegisteredUsingTypeOnly()
		{
			HashAlgorithmFactory.Register<FakeHashAlgorithm>();

			VerifyHashAlgorithm(FakeProviderName, FakeExpectedHash);
		}

		[TestMethod]
		public void RegisterAnHashAlgorithmMoreThanOneThrowException()
		{
			HashAlgorithmFactory.Register<FakeHashAlgorithm>(FakeProviderName);

			Executing
				.This(() => HashAlgorithmFactory.Register<FakeHashAlgorithm>("Fake1"))
				.Should().Throw<ArgumentException>();
		}

		[TestMethod]
		public void RegisterAnHashAlgorithmWithAnExistingProviderName()
		{
			Executing
				.This(() => HashAlgorithmFactory.Register<FakeHashAlgorithm>(HashAlgorithms.Md5))
				.Should().Throw<ArgumentException>();
		}

		[TestMethod]
		public void RegisterAnHashAlgorithmWithAnExistingProviderNameIgnoringCase()
		{
			Executing
				.This(() => HashAlgorithmFactory.Register<FakeHashAlgorithm>("Md5"))
				.Should().Throw<ArgumentException>();
		}

		[TestMethod]
		public void ComputeHexHashWithMd5Encoding()
		{
			var hex = HashAlgorithmFactory.Md5.ComputeHexWithEncoding("value");
			hex.Should().Be.EqualTo("2063c1608d6e0baf80249c42e2be5804");
		}

		[TestMethod]
		public void VerifyMd5Algorithm()
		{
			const string ExpectedHash = "7Y3u9bpuBzHQwB7nxLr8Ng==";
			VerifyHashAlgorithm(HashAlgorithms.Md5, ExpectedHash);
		}

		[TestMethod]
		public void VerifySha1Algorithm()
		{
			const string ExpectedHash = "0u/LuhAu0zOZR+hfQUHrCJJuQOk=";
			VerifyHashAlgorithm(HashAlgorithms.Sha1, ExpectedHash);
		}

		[TestMethod]
		public void VerifySha256Algorithm()
		{
			const string ExpectedHash = "oH5Pc0MkbIKybzLlb4VBjVGNiy8trnfx1W/nr1Dbl68=";
			VerifyHashAlgorithm(HashAlgorithms.Sha256, ExpectedHash);
		}

		[TestMethod]
		public void VerifySha384Algorithm()
		{
			const string ExpectedHash = "dCjqVkkhwOH1ySfU5y8MSgHZp6w9eiBMWp0wQKiCSZUwkNl2P+2hc/7j+nH3Xifd";
			VerifyHashAlgorithm(HashAlgorithms.Sha384, ExpectedHash);
		}

		[TestMethod]
		public void VerifySha512Algorithm()
		{
			const string ExpectedHash = "LF8Vx4KVZMMq9w2a7nOJveXQVEU0AQyQWNDXoMx95JZW4GdAQaCQe4CxsF4YtFm1Qoro7g9DpoDwki7j0A5qFA==";
			VerifyHashAlgorithm(HashAlgorithms.Sha512, ExpectedHash);
		}

		[TestMethod]
		public void VerifyRipemd160Algorithm()
		{
			const string ExpectedHash = "VOZX8jLSWWt2TFqUOIZgK4oj1yc=";
			VerifyHashAlgorithm(HashAlgorithms.Ripemd160, ExpectedHash);
		}

		private void VerifyHashAlgorithm(string providerName, string expectedHash)
		{
			var computeHash =
				HashAlgorithmFactory
					.Create(providerName)
					.ComputeHash("Hello");

			var hashString = Convert.ToBase64String(computeHash);

			hashString
				.Should().Be.EqualTo(expectedHash);
		}

		private class FakeHashAlgorithm : HashAlgorithm
		{
			public override void Initialize()
			{
			}

			protected override void HashCore(byte[] array, int ibStart, int cbSize)
			{
			}

			protected override byte[] HashFinal()
			{
				return FakeProviderName.ToByteArray();
			}
		}
	}
}